package ngat.oss.simulation;

import java.util.*;

import ngat.util.*;
import ngat.phase2.*;

/** Basic implementation of a SelectionHeuristic, chooses the group with 
 * the highest score. This needs setting up with parameters to determine the bias fn.
 * Initially hard coded levels and base value.
 */ 
public class BiasedSelectionHeuristic implements SelectionHeuristic {
    
    /** Bias threshold array.*/
    double[] biasThreshold;

    /** Create a BiasedSelectionHeuristic.*/
    public BiasedSelectionHeuristic() {

    }

    public BiasParams createBiasParams(ConfigurationProperties config) throws Exception {
	
	int nb = config.getIntValue("bias.count");
	double[] bt = new double[nb];
	for (int i = 0; i < nb; i++) {
	    double abt = config.getDoubleValue("bias."+i+".threshold");
	    bt[i] = abt;
	}

	BiasParams bp = new BiasParams();
	bp.biasThreshold = bt;
	return bp;
    }

    /** Configure from supplid bias params - typically for progrmatic configuration.*/
    public void configure(BiasParams bp) throws Exception {	
	biasThreshold = bp.biasThreshold;	
    }

    public Metric getBestGroup(List candidates) {
	
	Metric best = new Metric(null, -999);
	best.generator = "BiasedSelectionHeuristic.Test";

	Set rankedCandidates = new TreeSet(new ScoreRankedComparator());
	// populate the rank set with score-ranked candidates
	Iterator it = candidates.iterator();
	while (it.hasNext()) {

	    Metric test = (Metric)it.next();
	    rankedCandidates.add(test);	  
	}

	// threshold level increases for each rank
	// r=1, t=0.1; r=2, t=0.8; r=3, t=0.95
	// now select with ranked order.
	// the last lot are equally likely - this shouldnt happen often though !
	int is = 0;
	Iterator rl = rankedCandidates.iterator();
	while (rl.hasNext()) {
	    double t = (is < 4 ? biasThreshold[is] : 1.0);
	    best = (Metric)rl.next();
	    double r = Math.random();
	    if (r < t)
		return best;   
	    is++; // on rank [is-1] 
	}

	// only if we didnt pick anything yet 
	// - note the last one may have slipped past the threshold test but we
	// catch it here anyway.
	return best;

    }
 
    /** Returns the probability of the test metric being selected from the supplied candidate metrics.
     * The test metric must not already be present in the candidate list.
     */
    public double getSelectionProbability(List candidates, Metric test) {

	// First find the test metric's rank by counting no which score better.
	int count = 0;
        Iterator ic = candidates.iterator();
        while (ic.hasNext()) {
            Metric metric = (Metric)ic.next();
            if (metric.score > test.score)
                count++;
        }
	int rank = count+1;
        
	// apply a simple smoothing function reflecting the fact that r=0 has high probability, 
	// r=1 less and so forth but with P decreasing quite slowly and never quite reaches zero
	return 1.0/(1.0+Math.exp(rank - 2.0)/5.0);
	
    }

    private class ScoreRankedComparator implements Comparator {
	
	ScoreRankedComparator() {}
	
	public int compare(Object o1, Object o2) {
	    
	    Metric m1 = (Metric)o1;
	    Metric m2 = (Metric)o2;

	    if (m1.score < m2.score)
		return -1;
	    if (m1.score > m2.score)
		return 1;
	    return 0;
	}
	
    }
}
